package com.ease.util {

   /**
    * Utility class which permits making relatively precise measurements
    * (up to millisecond level) of code execution time periods.
    */
   public class Stopwatch {

      /**
       * Start time
       */
      private var _t1:Date = null;

      /**
       * End time
       */
      private var _t2:Date = null;

      /**
       * Helps us track the current state of the stopwatch
       */
      private var _running:Boolean = false;

      /**
       * Whether or not to auto start a time after a call to "toString" method.
       * Usefull, because it can save you manally calling <code>start</code>
       * in loops or sequences.
       * e.g. The code below will time correctly both sections separately, event though
       * they use the same timer, because of the <code>autoRestart</code> after each call
       * to <code>Stopwatch.toString</code> from the logger.
       * <listing version="3.0">
       *    var sw:Stopwatch = new Stopwatch(true);
       *    .... do something here...
       *    _logger.debug("doing something took: {0}", sw);
       *    .... do something more here ....
       *    _logger.debug("doing something more took: {0}, sw);
       * </listing>
       * The other benefit is in loops:
       * <listing version="3.0">
       *    var swPerIteration:Stopwatch = new Stopwatch(true);
       *    for (var i:uint = 0; i < range.lenght; i++) {
       *       doSomething(i);
       *       _logger.debug("Iteration {0} took {1}", i, swPerIteration);
       *    }
       * </listing>
       */
      public var autoRestart:Boolean = false;

      /**
       * Constructs a <code>Stopwatch</code> object
       * @see #autoRestart
       */
      public function Stopwatch(autoRestart:Boolean = false):void {
         this.autoRestart = autoRestart;
         this.start();
      }

      /**
       * Starts the time measurement. Note that the stopwatch creation actually
       * starts it automatically (for convenience).
       * So you will want to call <code>start()</code> explicitly only after you
       * called <code>stop</code> or if you want to restart the time measurement
       * prematurely.
       */
      public function start():void {
         // We can start or restart at any time. No need to be stopped first.
         _running = true;
         _t1 = new Date();
         _t2 = _t1;
      }

      /**
       * Stops the time measurement.
       * It is safe to call this multiple times - only the first call will stop the
       * timer. The rest of the calls will do nothing.
       */
      public function stop():void {
         // In order to stop, you need to be running.
         if (_running) {
            _t2 = new Date();
            _running = false;
         }
      }

      /**
       * Returns the elapsed time as a <code>TimeSpan</code> object. You need to call
       * <code>stop</code> before you can safely use this method.
       * You can use its formating methods directly this way, if you want to do so.
       * If you want to get the milliseconds you can use the following code:
       * <listing version="3.0">
       *    var sw:Stopwatch = new Stopwatch();
       *    .... do something here ...
       *    sw.stop();
       *    // getElapsedTime() returns a TimeSpan object
       *    var milliseconds:uint = sw.getElapsedTime().totalMilliseconds;
       * </listing>
       */
      public function getElapsedTime():TimeSpan {
         return TimeSpan.fromDates(_t1, _t2);
      }

      /**
       * A convenience method to let you easily print a stopwatch value. It will
       * allow for this style of coding:
       * <listing version="3.0">
       *    var time:Stopwatch = new Stopwatch();
       *    ...
       *    doSomething();
       *    ...
       *    // This one will call Stopwatch.toString() which will stop the timing
       *    // and print the amout of time for the operation. No need to call
       *    // time.stop() explicitly.
       *    _logger.debug(time);
       * </listing>
       */
      public function toString():String {
         stop();

         // Formating takes time, so we need to do it before we restart the stopwatch.
         var retVal:String = getElapsedTime().toString();

         if (autoRestart) {
            start();
         }

         return retVal;
      }
   } // class
} // package